module arm.bitband;


import arm.conf;
import std.traits;

// 确认可用前提
static if(SupportBitBand) package{

    /// 可用bitband
    static if(__traits(targetCPU) == "cortex-m4") 
    enum Bandables = [
        // Peripheral reg
        BitBandable(0x4000_0000u,0x000F_FFFFu),
        // SRAM 
        BitBandable(0x2000_0000u,0x000F_FFFFu),
    ];

    // 内存映射结构表
    struct BitBandable{
        /// 开始地址
        size_t RegionStart;
        /// 范围宽度
        size_t RegionSize;
        /// 结束地址
        //pragma(inline,true)
        @property size_t RegionEnd() @nogc pure
        {
            return ((RegionStart + RegionSize) - 1);
        }
    }

    /**
    * 检查地址是否可以使用`bitband`方式操作
    * Params:
    *  addr = 要操作的地址
    * Returns: true = 可以使用`bitband`方式操作
    */
    static bool CheckBitBandable(size_t addr) @nogc pure
    {
        return (
            ((addr >= Bandables[0].RegionStart) && (addr <= Bandables[0].RegionEnd)) ||
            ((addr >= Bandables[1].RegionStart) && (addr <= Bandables[1].RegionEnd))
            );
    }
    template CheckBitBandable(size_t addr)
    {
        enum CheckBitBandable = (
            ((addr >= Bandables[0].RegionStart) && (addr <= Bandables[0].RegionEnd)) ||
            ((addr >= Bandables[1].RegionStart) && (addr <= Bandables[1].RegionEnd))
            );
    }
    /**
    * 转换独立位操作方式为`bitband`方式
    * Params:
    *  addr = 要操作的地址
    *  bitnum = 要操作的位
    */
    size_t BitBand(in size_t addr,in size_t bitnum)  @nogc pure
    in(bitnum < 32)
    in(CheckBitBandable(addr))
    {
        return (addr & 0xF0000000) + 0x0200_0000 + ((addr & 0x000FFFFF) << 5) + (bitnum << 2);
    }
    template BitBand(size_t addr,size_t bitnum) 
    if((bitnum < 32) && CheckBitBandable!(addr))
    {       
        enum BitBand = (addr & 0xF0000000) + 0x0200_0000 + ((addr & 0x000FFFFF) << 5) + (bitnum << 2); 
    }
}


package{
    import core.attribute:optStrategy;
    /**
    * 寄存器读写操作,默认寄存器操作宽度是uint
    */
    @optStrategy("optsize")
    pragma(inline, true)
    struct volatile(T)
    {
        import core.atomic;
        import arm.misc;

        private shared(T) _storage;
        /// 内存指针
        private size_t paddr() @property 
        {
            return cast(size_t)&this;
        }
        @disable this();
        /// 读取寄存器值
        @property 
        T value() const
        {
            return atomicLoad(_storage);
        }
        alias value this;
        /// 直接送出原始状态
        @property 
        ref T orig() => cast(T)_storage;
        /// 获取指针
        @property
        T* ptr() const
        {
            return cast(T*)&_storage;
        }
        /// 重载
        void opOpAssign(string op)(in T rhs) 
        {
            atomicOp!(op~"=")(_storage, rhs);
        }
        void opAssign()(const T rhs)
        {
            atomicStore(_storage, rhs);
        }
        bool opEquals()(const T rhs) const
        {
            return atomicOp!`==`(_storage,rhs) != 0;
        }
        int opCmp()(const T rhs) const
        {
            return atomicOp!"=="(_storage, rhs);
        }
        /// `$` 常量定义
        @property int opDollar(size_t dim : 0)() 
        { 
            return (this.sizeof*8) - 1; 
        }
        /// 位处理
        @property int opIndex(size_t pos)
        {
            auto pbitband = BitBand(this.paddr, pos);
            
            return atomicLoad(*cast(shared(T)*)pbitband);
        }
        @property void opIndexAssign(T v,size_t pos)
        {
            auto pbitband = BitBand(this.paddr, pos);
            atomicStore(*cast(shared(T)*)pbitband, v);
        }
        /// 位域处理
        T opSlice(size_t l,size_t h)
        in(l<=h)
        in(h < T.sizeof*8)
        {
            auto ret = this.value;
            ret <<= h;              /// 去除无效位信息
            ret >>= (h+l);          /// 对齐0位
            return ret; 
        }
        void opSliceAssign(T v, size_t l, size_t h)
        in(l<=h)
        in(h < T.sizeof*8)
        in(v <= (MakerMask(l,h) >> l))
        {
            auto mask = MakerMask(l,h);
            atomicOp!`&=`(_storage, ~mask);
            atomicOp!`|=`(_storage, (v << l));
        }
    }
}


